﻿using System.Collections.Generic;
using System.ServiceModel;
using System.Threading;
using PSE.ProcessManager.Common.Process;
using System.Runtime.Serialization;
using PSE.ProcessManager.Entity;

namespace PSE.ProcessManager.Common.Gateway
{
    [ServiceBehavior(
        ConcurrencyMode = ConcurrencyMode.Multiple,
        InstanceContextMode = InstanceContextMode.PerCall)]
    [KnownTypeAttribute(typeof(Process.ProcessStatus))]
    public class ListenerService : IListenerService
    {
        public class Actions
        {
            public const string SendMessage = "SendMessage";
            public const string SendMessageCallBack = "SendMessageCallBack";
        }

        public class ReplyActions
        {
            public const string SendMessage = "SendMessageResponse";
            public const string SendMessageCallBack = "SendMessageCallBackResponse";
        }

        private static List<IListenerServiceCallback> _callbackList = new List<IListenerServiceCallback>();

        #region Delegates

        /// <summary>
        /// Delegate disparado ao receber uma mensagem.
        /// </summary>
        public static ServiceEvents.MessageReceived OnReceiveMessage { get; set; }

        /// <summary>
        /// Delegate disparado ao estabelecer uma conexão com o Client.
        /// </summary>
        public static ServiceEvents.ClientConnected OnConnect { get; set; }

        /// <summary>
        /// Delegate disparado ao receber um pedido de desconexão do Client.
        /// </summary>
        public static ServiceEvents.ClientDisconnected OnDisconnect { get; set; }

        /// <summary>
        /// Delegate disparado ao receber uma solicitação de verificação de execução do processo.
        /// </summary>
        public static ServiceEvents.IsProcessRunning IsProcessRunning { get; set; }

        /// <summary>
        /// Delegate para o evento dispara quando o client solicita a lista do log de um processo
        /// </summary>
        public static ServiceEvents.RetrievedLogList OnRetrieveLogList { get; set; }

        /// <summary>
        /// Delegate para o evento dispara quando o client solicita o ultimo log de um processo
        /// </summary>
        public static ServiceEvents.RetrievedLastProcessLog OnRetrieveLastProcessLog { get; set; }

        #endregion

        #region IListenerService Members

        public void SendMessage(System.ServiceModel.Channels.Message message)
        {
            BaseMessage baseMessage = new BaseMessage();
            ProcessMessageHeader header = message.Headers.GetHeader<ProcessMessageHeader>(ProcessMessageHeader.Name, ProcessMessageHeader.NameSpace);
            baseMessage.MessageId = header.MessageId;
            baseMessage.Operation = header.Operation;
            baseMessage.ProcessId = header.ProcessId;
            baseMessage.From = header.From;
            baseMessage.To = header.To;

            baseMessage.Original = message.CreateBufferedCopy(int.MaxValue);

            if (OnReceiveMessage != null)
            {
                OnReceiveMessage(baseMessage);
            }
        }

        public void Connect()
        {
            IListenerServiceCallback request = OperationContext.Current.GetCallbackChannel<IListenerServiceCallback>();

            (request as ICommunicationObject).Closed += new System.EventHandler(ListenerService_Closed);

            if (!_callbackList.Contains(request))
                _callbackList.Add(request);

        }

        void ListenerService_Closed(object sender, System.EventArgs e)
        {
            if (OnDisconnect != null)
                OnDisconnect((IListenerServiceCallback)sender);
        }

        public void ConnectToProcess(int processId, string clientIdentifier)
        {
            IListenerServiceCallback request = OperationContext.Current.GetCallbackChannel<IListenerServiceCallback>();

            (request as ICommunicationObject).Closed += new System.EventHandler(ListenerService_Closed);

            if (OnConnect != null)
            {
                OnConnect(processId, clientIdentifier, request);
            }
        }

        public void ConnectToListenMessages(List<int> messageIds)
        {
            //Not yet.
        }

        public void Disconnect()
        {
            IListenerServiceCallback request = OperationContext.Current.GetCallbackChannel<IListenerServiceCallback>();

            if (OnDisconnect != null)
            {
                OnDisconnect(request);
            }

        }

        public bool IsProcessExecuting(int processId)
        {
            if (IsProcessRunning != null)
                return IsProcessRunning(processId) > -1;

            return false;
        }

        public int GetProcessPid(int processId)
        {
            if (IsProcessRunning != null)
                return IsProcessRunning(processId);

            return -1;
        }

        public List<LogEntity> RetrieveLogList(int processId)
        {
            if (OnRetrieveLogList != null)
            {
                return OnRetrieveLogList(processId);
            }

            return null;
        }

        public LogEntity RetrieveLastProcessLog(int processId)
        {
            if (OnRetrieveLastProcessLog != null)
            {
                return OnRetrieveLastProcessLog(processId);
            }

            return null;
        }

        public void KeepAlive()
        {
            // Não faz nada, apenas mantem a conexão
        }

        #endregion

        #region Not Implemented IListenerService Members


        public System.IAsyncResult BeginSendMessage(System.ServiceModel.Channels.Message message, System.AsyncCallback callback, object asyncState)
        {
            throw new System.NotImplementedException();
        }

        public void EndSendMessage(System.IAsyncResult result)
        {
            throw new System.NotImplementedException();
        }

        public System.IAsyncResult BeginConnectToProcess(int processId, string clientIdentifier, System.AsyncCallback callback, object asyncState)
        {
            throw new System.NotImplementedException();
        }

        public void EndConnectToProcess(System.IAsyncResult result)
        {
            throw new System.NotImplementedException();
        }

        #endregion

    }
}